#pragma once

#ifdef _WIN32
#include <windows.h>

#else
// Linux

#endif // _WIN32


#include <iostream>
using std::cout;
using std::endl;

//template<size_t N>
//class ObjectPool
//{
//public:
//
//};


inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32

	// 使用系统调用 ， 脱离 malloc 
	void* ptr = VirtualAlloc(0, kpage * (1 << 13), MEM_COMMIT | MEM_RESERVE,   // 8 K
		PAGE_READWRITE);
#else
	// linux下brk mmap等
#endif
	if (ptr == nullptr)
		throw std::bad_alloc();
	return ptr;
}

// 定长池的设计实现
template<typename T>
class ObjectPool
{
	//void* _memory;   // 为什么不用 void*  ,void* 不能解引用，不能++ ,指针得 解引用，++，才有意义
	char* _memory = nullptr;  // 使用 char* 而非 void* , char* 好用，方便从大块内存切出来一部分
	void* _freeList = nullptr;  // 指向归还回来的空闲区块
	size_t _remainBytes = 0;   // 大内存块在切分中剩余的字节数

public:
	T* New()
	{
		T* obj = nullptr;  //待分配区域的首地址

		if (_freeList != nullptr)
		{
			// 有空闲区块

			void* next = *(void**)_freeList;   // next 指向下一个空闲区间
			// 这里  *(void**) 可以解决 32位机器和 64 位机器下，指针占用4字节和8字节不同的问题
			// 强转成 void** 二级指针，再解引用成一级指针(void*)
			// 在32位下，指针位 4位，自动分配前4字节 
			// 在64位下，指针位 8位，自动分配前8字节 
			obj = (T*)_freeList;
			_freeList = next;
		}
		else
		{
			// 没有空闲区块
			
			if (_remainBytes < sizeof T)
			{
				// 但剩余的内存不够一个对象的大小时，则重新开辟更大的空间
				_remainBytes = 128 * 1024;  // 128 kb
				//_memory = (char*)malloc(_remainBytes);
				_memory = (char*)SystemAlloc(_remainBytes>>13);   // 除以 8k , 得到页号
				if (_memory == nullptr)
				{
					throw std::bad_alloc();  // 分配失败
				}
			}

			obj = (T*)_memory;

			// 这一步防止归还的内存连指针都无法存下
			size_t objSize = sizeof T < sizeof(void*) ? sizeof(void*) : sizeof T;
			_memory += objSize;  // memory 指向下一个未分配的内存块
			_remainBytes -= objSize;
		}

		// 定位 new，将分配的内存传递给new，new再去调用其构造函数
		new(obj)T; 

		return obj;
	}


	void Delete(T* obj)
	{
		//if (_freeList == nullptr)
		//{
		//	*(void**)obj = nullptr;  // 将内存中前 4个字节修改为 nullptr
		//	_freeList = obj;
		//}
		//else
		//{
		//	*(void**)obj = _freeList;   // 头插,将新释放的元素的内存前 4个字节的内容修改为指向下一个空闲空间的地址
		//	_freeList = obj;
		//}

		* (void**)obj = _freeList;   // 头插,将新释放的元素的内存前 4个字节的内容修改为指向下一个空闲空间的地址
		_freeList = obj;
	}
};

